package com.vf.admin.common.annotation;

import com.vf.admin.client.common.ClientConstants;
import com.vf.admin.client.common.RedisKeys;
import com.vf.admin.common.redis.RedisUtil;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Aspect
@Component
public class RedisSynchronizedAspect {

    @Autowired
    private RedisUtil redisUtil;
    private ExpressionEvaluation<String> evaluator = new ExpressionEvaluation<>();

    /**
     * 方法上有@RedisSynchronized注解均会调用此方法
     *
     * @param point
     * @param redisSynchronized
     * @return
     */
    @Around("@annotation(redisSynchronized)")
    @SneakyThrows
    public Object around(ProceedingJoinPoint point, RedisSynchronized redisSynchronized) {
        // 获取自定义key，作为redis的key
        String key = "";
        if(redisSynchronized.isSpEL()) {
            key = getKey(point);
        } else {
            key = redisSynchronized.key();
        }
        // 获取请求设置多少秒不可重复提交(redis过期时间)
        long second = redisSynchronized.second();
        // 获取异常提示信息
        String msg = redisSynchronized.msg();
        // 获取是否需要在key上拼接用户ID
        boolean splicingUserId = redisSynchronized.splicingUserId();
        if (splicingUserId) {
            // 获取用户ID，根据项目的登录模块，自行定义获取用户的方法
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            Integer userId = getUserId(request.getHeader(ClientConstants.client_toke));
            key += "-" + userId;
        }
        // 根据当前key查询redis是否有值
        Object o = redisUtil.get(key);
        if (o == null) {
            // 没值，继续进入接口，将当前key存入redis，指定过期时间
            redisUtil.set(key, "arbitrarily", second);
            return point.proceed();
        } else {
            // 有值，此处抛出全局异常
            throw new Exception(msg);
        }
    }

    /**
     * 获取用户ID
     *
     * @return
     */
    public Integer getUserId(String token) {
        Integer userId = (Integer) redisUtil.get(RedisKeys.userToken + token);
        if (userId == null)
            return null;
        return userId;
    }

    /**
     * SpEL 表达式的解析
     *
     * @param point
     * @return
     */
    public String getKey(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        RedisSynchronized redisSynchronized = method.getAnnotation(RedisSynchronized.class);
        if (point.getArgs() == null) {
            return null;
        }
        EvaluationContext evaluationContext = evaluator.createEvaluationContext(point.getTarget(), point.getTarget().getClass(), ((MethodSignature) point.getSignature()).getMethod(), point.getArgs());
        AnnotatedElementKey methodKey = new AnnotatedElementKey(((MethodSignature) point.getSignature()).getMethod(), point.getTarget().getClass());
        return evaluator.condition(redisSynchronized.key(), methodKey, evaluationContext, String.class);
    }
}
